其他
对VM逆向的分析 | 一个经典的虚拟机逆向CTF题
一 • WriteUp
%2007d4e31ff53d45308e7309126226c678/L0__6X1NGME7LX43BB.png)
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // [esp+10h] [ebp-8h]
sub_6914F0();
while ( 1 )
{
LABEL_2:
for ( i = 0; ; i += 2 )
{
if ( i >= 0x58 )
goto LABEL_2;
if ( dword_6D48F0[4 * i] == opcode_team[ei_p] )// opcode_team存在于内存中的伪机器码(指令)
break;
}
dispatcher[i](); // 模拟一个CPU不断的去执行指令(其实就是根据opcode去执行一些模拟汇编的一些函数)
}
}
这个模拟的就是push data,同时我们也在内存中找到了模拟栈。
opcode_team = [0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x01, 0x11, 0x0C, 0x00, 0x01, 0x0D, 0x0A, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x11, 0x0C, 0x00, 0x02, 0x0D, 0x2B, 0x00, 0x14, 0x00, 0x02, 0x01, 0x01, 0x61, 0x0C, 0x00, 0x01, 0x10, 0x1A, 0x00, 0x01, 0x01, 0x7A, 0x0C, 0x00, 0x01, 0x0F, 0x1A, 0x00, 0x01, 0x01, 0x47, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x00, 0x01, 0x0B, 0x24, 0x00, 0x01, 0x01, 0x41, 0x0C, 0x00, 0x01, 0x10, 0x24, 0x00, 0x01, 0x01, 0x5A, 0x0C, 0x00, 0x01, 0x0F, 0x24, 0x00, 0x01, 0x01, 0x4B, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x00, 0x01, 0x01, 0x01, 0x10, 0x09, 0x00, 0x01, 0x03, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x0B, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x00, 0x00, 0x02, 0x09, 0x00, 0x02, 0x05, 0x00, 0x02, 0x0F, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0x02, 0x03, 0x00, 0x02, 0x03, 0x00, 0x02, 0x01, 0x00, 0x02, 0x07, 0x00, 0x02, 0x07, 0x00, 0x02, 0x0B, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x07, 0x00, 0x02, 0x02, 0x00, 0x02, 0x0C, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x13, 0x01, 0x02, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x0E, 0x5B, 0x00, 0x01, 0x01, 0x22, 0x0C, 0x02, 0x01, 0x0D, 0x59, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x4E, 0x00, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00]
opcode_key = {
0: 'nop',
1: 'mov reg data',
2: 'push data',
3: 'push_reg',
4: 'pop_reg',
5: 'printf',
6: 'add_reg_reg1',
7: 'sub_reg_reg1',
8: 'mul',
9: 'div',
10: 'xor',
11: 'jmp',
12: 'cmp',
13: 'je',
14: 'jne',
15: 'jg',
16: 'jl',
17: 'scanf_strlen',
18: 'mem_init',
19: 'stack_to_reg',
20: 'load_input',
0xff: 'exit'}
count = 0
code_index = 1
for x in opcode_team: # 取每个opcode
if count % 3 == 0: # 每3个opcode是一条指令,每个指令的第一个是操作码
print(str(code_index)+':', end='')
print(opcode_key[x], end=' ')
code_index += 1
elif count % 3 == 1:
print(str(x)+',', end=' ')
else:
print(str(x))
count += 1
//打印出来的结果如下
1:mov reg data 3, 3
2:printf 0, 0
3:scanf_strlen 0, 0
4:mov reg data 1, 17
5:cmp 0, 1
6:je 10, 0
7:mov reg data 3, 1
8:printf 0, 0
9:exit 0, 0
10:mov reg data 2, 0
11:mov reg data 0, 17
12:cmp 0, 2
13:je 43, 0
14:load_input 0, 2
15:mov reg data 1, 97
16:cmp 0, 1
17:jl 26, 0
18:mov reg data 1, 122
19:cmp 0, 1
20:jg 26, 0
21:mov reg data 1, 71
22:xor 0, 1
23:mov reg data 1, 1
24:add_reg_reg1 0, 1
25:jmp 36, 0
26:mov reg data 1, 65
27:cmp 0, 1
28:jl 36, 0
29:mov reg data 1, 90
30:cmp 0, 1
31:jg 36, 0
32:mov reg data 1, 75
33:xor 0, 1
34:mov reg data 1, 1
35:sub_reg_reg1 0, 1
36:mov reg data 1, 16
37:div 0, 1
38:push_reg 1, 0
39:push_reg 0, 0
40:mov reg data 1, 1
41:add_reg_reg1 2, 1
42:jmp 11, 0
43:push data 7, 0
44:push data 13, 0
45:push data 0, 0
46:push data 5, 0
47:push data 1, 0
48:push data 12, 0
49:push data 1, 0
50:push data 0, 0
51:push data 0, 0
52:push data 13, 0
53:push data 5, 0
54:push data 15, 0
55:push data 0, 0
56:push data 9, 0
57:push data 5, 0
58:push data 15, 0
59:push data 3, 0
60:push data 0, 0
61:push data 2, 0
62:push data 5, 0
63:push data 3, 0
64:push data 3, 0
65:push data 1, 0
66:push data 7, 0
67:push data 7, 0
68:push data 11, 0
69:push data 2, 0
70:push data 1, 0
71:push data 2, 0
72:push data 7, 0
73:push data 2, 0
74:push data 12, 0
75:push data 2, 0
76:push data 2, 0
77:mov reg data 2, 1
78:stack_to_reg 1, 2
79:pop_reg 0, 0
80:cmp 0, 1
81:jne 91, 0
82:mov reg data 1, 34
83:cmp 2, 1
84:je 89, 0
85:mov reg data 1, 1
86:add_reg_reg1 2, 1
87:jmp 78, 0
88:mov reg data 3, 0
89:printf 0, 0
90:exit 0, 0
91:mov reg data 3, 1
92:printf 0, 0
93:exit 0, 0
94:nop
分析和注释:
mov edx, 3
printf(...)
scanf(our_input)
mov eax, strlen(our_input)
mov ebx, 17
cmp eax, ebx
je (10) //相等才跳转到position0
mov edx, 1
printf(...)
exit(0) //不相等就退出了
(10)mov ecx, 0
mov eax, 17
cmp eax, ecx
je (43) //直至计数器等于17
eax = load_input[ecx] //循环读取我们的input
mov ebx, 'a'
cmp eax, ebx
jl (26) //该字符小于'a'则跳转到26
mov ebx, 'z'
cmp eax, ebx
jg (26) //该字符大于'z'则跳转到26
mov ebx, 71
xor eax, ebx
mov ebx, 1
add eax, ebx //否则将该字符(char^71+1)
jmp (36)
(26)mov ebx, 'A'
cmp eax, ebx
jl (36) //该字符比'A'小则跳转到36
mov ebx, 'Z'
cmp eax, ebx
jg (36) //该字符比'Z'大则跳转到36
mov ebx, 75
xor eax, ebx
mov ebx, 1
sub eax, ebx //否则将该字符(char^75-1)
(36)mov ebx, 16
div eax, ebx //将该字符%16
push ebx
push eax //先压入整除的数字,再压入余数
mov ebx, 1
mov ecx, 1
jmp(11)
(43)push //压入一连串的数据,
...
...
(76)push 2
(77)mov ecx, 1
(78)stack_to_reg 1, 2
pop eax
cmp eax, ebx
jne (91)
mov ebx, 34 //17拆成34个(整数和余数)
cmp ecx, ebx
je (89)
mov ebx, 1
mov ecx, 1
jmp(78)
mov edx, 0 //设置返回值为0
(89)printf(..) //打印失败字符串
exit(..) //退出程序
(91)mov edx, 1 //设置返回值为1
printf(..) //打印成功字符串
# exit(..) //退出程序
nop
最后理解程序的逻辑之后写出解题脚本:
data = [0x7,0xd,0x0,0x5,0x1,0xc,0x1,0x0,0x0,0xd,0x5,0xf,0x0,0x9,0x5,0xf,0x3,0x0,0x2,0x5,0x3,0x3,0x1,0x7,0x7,0xb,0x2,0x1,0x2,0x7,0x2,0xc,0x2,0x2,]
data = data[::-1]
flag = ''
for i in range(0, 34, 2):
temp = data[i] + data[i+1]*16
x = ((temp+1) ^ 75)
y = ((temp-1) ^ 71)
if x>=65 and x<=90: # 'A'-'Z'
flag += chr(x)
elif y>=97 and y<=122: # 'a' - 'z'
flag += chr(y)
else:
flag += chr(temp) # 没有处于'a'-'z'或'A'-'Z'之间
print(flag)
二 • 总结
https://www.bilibili.com/video/BV1Nv411k7HJ
https://blog.csdn.net/weixin_43876357/article/details/108488762
看雪ID:SYJ-Re
https://bbs.pediy.com/user-home-921830.htm
*本文由看雪论坛 SYJ-Re 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!